//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<rect.h>
///	@path	~/src/lib/math/
///	@date	2007/12/07
///	@desc	Rectangle region class.

#pragma once

#include "xeres.h"

#include "lib/math/math_ext.h"
#include "lib/math/pair.h"

namespace xeres {

	// rect region access index
	enum RectIndex
	{
		RECT_XMIN = 0 ,
		RECT_YMIN = 1 ,
		RECT_XMAX = 2 ,
		RECT_YMAX = 3 ,
	};

	/*!
		\class	Rect
		\brief	Rectangle region of 2d space.
	*/
	template<
		typename _Ty
	> class Rect : public ArithmeticOp< _Ty >
	{
	public:

		/// \ctor
		Rect( void )
			: rmin( ArithmeticOp< _Ty >::infinite() )
			, rmax( ArithmeticOp< _Ty >::ninfinite() )
		{
		}

		/// \ctor_set
		template<
			typename _T1 , typename _T2 , typename _T3 , typename _T4
		> Rect( _T1 left , _T2 top , _T3 right , _T4 bottom )
			: rmin( left , top )
			, rmax( right , bottom )
		{
			validate();
		}

		/// \ctor_set
		template<
			typename _T1 , typename _T2
		> Rect( const Pair<_T1>& pmin , const Pair<_T2>& pmax )
			: rmin( pmin )
			, rmax( pmax )
		{
			//validate();
		}

		/// \ctor_set
		template<
			typename _T1 , typename _T2 , typename _T3
		> Rect( _T1 x , _T2 y , const Pair<_T3>& pmax )
			: rmin( x , y )
			, rmax( pmax )
		{
			//validate();
		}

		/// \ctor_copy
		Rect( const Rect& r )
			: rmin( r.rmin )
			, rmax( r.rmax )
		{
		}

		/// \ctor_copy
		template<
			typename _TOther
		> Rect( const Rect<_TOther>& r )
			: rmin( r.rmin )
			, rmax( r.rmax )
		{
		}

	public:

		// set method
		template<
			typename _T1 , typename _T2 , typename _T3 , typename _T4
		> void set( _T1 left , _T2 top , _T3 right , _T4 bottom )
		{
			rmin.set( left , top );
			rmax.set( right , bottom );
			validate();
		}

		// copy method
		template<
			typename _Other
		> void copy( const _Other tile[4] ) {
			for( int i = 0 ; i < 4 ; ++i )
				f[i] = tile[i];
			validate();
		}

		// bound n points
		template<typename _TOther> void bound( const Pair<_TOther> * p , size_t count )
		{
			rmin = *p;
			rmax = *p;
			for( size_t i = 1 ; i < count ; ++i )
			{
				rmin = min( rmin , p[i] );
				rmax = max( rmax , p[i] );
			}
		}

		// empty box
		void empty( void )
		{
			rmin.single( ArithmeticOp< _Ty >::infinite() );
			rmax.single( ArithmeticOp< _Ty >::ninfinite() );
		}

		// offset
		void offset( const Pair<int>& org )
		{
			assert( !is_empty() );
			rmin += org;
			rmax += org;
		}

		// extend
		void extend( const Pair<int>& size )
		{
			assert( !is_empty() );
			rmin -= size;
			rmax += size;
		}

		// get min
		const Pair<_Ty>& get_min( void ) const
		{
//			assert( !is_empty() );
			return rmin;
		}
		// get max
		const Pair<_Ty>& get_max( void ) const
		{
//			assert( !is_empty() );
			return rmax;
		}

		// determine if box is not set
		bool is_empty( void ) const
		{
			return ( rmin.x > rmax.x ) || ( rmin.y > rmax.y );
		}

		// center
		Pair<_Ty> center( void ) const
		{
			assert( !is_empty() );
			return ( rmin + rmax ) / 2;
		}

		// distance from center to point
		template< typename _TOther > _Ty distance( const Pair<_TOther>& o ) const
		{
			assert( !is_empty() );
			return center().distance( o );
		}

		// union with a rect
		void union_rect( const Rect& r )
		{
			rmin = min( rmin , r.rmin );
			rmax = max( rmax , r.rmax );
		}

		// union with a point
		template< typename _TOther >
		void union_point( const Pair<_TOther>& p )
		{
			rmin = min( rmin , p );
			rmax = max( rmax , p );
		}

		// intersect a box
		template< typename _TOther >
		void set_intersection( const Rect<_TOther>& r )
		{
			assert( !is_empty() );
			assert( !r.is_empty() );
			rmin = max( rmin , r.rmin );
			rmax = min( rmax , r.rmax );
		}

		// union
		friend Rect operator | ( const Rect& a , const Rect& b )
		{
			assert( !a.is_empty() );
			assert( !b.is_empty() );
			Pair<_Ty> rmin;
			Pair<_Ty> rmax;
			rmin = min( a.rmin , b.rmin );
			rmax = max( a.rmax , b.rmax );
			return Rect( rmin , rmax );
		}

		// intersection
		friend Rect operator & ( const Rect& a , const Rect& b )
		{
			assert( !a.is_empty() );
			assert( !b.is_empty() );
			Pair<_Ty> rmin;
			Pair<_Ty> rmax;
			rmin = max( a.rmin , b.rmin );
			rmax = min( a.rmax , b.rmax );
			return Rect( rmin , rmax );
		}

		// overlap test
		template< typename _TOther > bool is_overlap( const Rect<_TOther>& b ) const
		{
			assert( !is_empty() );
			assert( !b.is_empty() );
			if( ( rmax.x < b.rmin.x ) || ( rmin.x > b.rmax.x ) )
				return false;
			if( ( rmax.y < b.rmin.y ) || ( rmin.y > b.rmax.y ) )
				return false;
			return true;
		}

		// inside test
		template< typename _TOther > bool is_inside( const Pair<_TOther>& p ) const
		{
			assert( !is_empty() );
			if( p[0] < rmin[0] ) return false;
			if( p[0] > rmax[0] ) return false;
			if( p[1] < rmin[1] ) return false;
			if( p[1] > rmax[1] ) return false;
			return true;
		}

		//@ set
		void set_left( _Ty l ) {
			rmin[0] = l;
		}
		void set_top( _Ty t ) {
			rmin[1] = t;
		}
		void set_right( _Ty r ) {
			rmax[0] = r;
		}
		void set_bottom( _Ty b ) {
			rmax[1] = b;
		}

		//@ accessors
		_Ty xmin( void ) const {
			return rmin.x;
		}
		_Ty xmax( void ) const {
			return rmax.x;
		}
		_Ty ymin( void ) const {
			return rmin.y;
		}
		_Ty ymax( void ) const {
			return rmax.y;
		}

		//@ attribute
		_Ty width( void ) const {
			return xmax() - xmin();
		}
		_Ty height( void ) const {
			return ymax() - ymin();
		}

		_Ty left( void ) const {
			return xmin();
		}
		_Ty top( void ) const {
			return ymin();
		}
		_Ty right( void ) const {
			return xmax();
		}
		_Ty bottom( void ) const {
			return ymax();
		}

		_Ty operator [] ( size_t index ) const {
			assert( index <= 4 );
			return static_cast<_Ty*>(this)[index];
		}

		// validate
		void validate( void ) const {
			assert( xmax() >= xmin() );
			assert( ymax() >= ymin() );
		}

		// make infinite
		static Rect<_Ty> infinite( void )
		{
			return Rect<_Ty>( ArithmeticOp< _Ty >::ninfinite() , ArithmeticOp< _Ty >::ninfinite() ,
				ArithmeticOp< _Ty >::infinite() , ArithmeticOp< _Ty >::infinite() );
		}

	private:

	//@ data
		Pair< _Ty >		rmin , rmax;
	};

	/// \brief Short cut of making rect.
	template< typename _Ty > static inline Rect<_Ty> make_rect( _Ty x , _Ty y , _Ty w , _Ty h )
	{
		return Rect<_Ty>( x , y , x + w , y + h );
	}

} // namespace xeres
